---
author: Marcus Rohrmoser
categories:
- en
- development
date: "2009-08-18T18:53:02+00:00"
tags:
- Asctime
- Cocoa
- Date
- HTTP Header
- If-Modified-Since
- iPhone
- Last-Modified
- NSDate
- NSDateFormatter
- NSHTTPURLResponse
- Objective C
- Request Header
- Response Header
- RFC1123
- RFC850
- SenTestingKit
title: NSDateFormatter & Http Header
type: post
url: /2009/08/nsdateformatter-http-header/
yourls_shorturl:
- http://s.mro.name/x
---

**A.S. Update 2, License**: _as I've been asked about which license this code is under: I put this
into [Public Domain][1]. No warranty whatsoever. Still I'd be happy about attribution but don't
require such._

Ever tried to get e.g. the [`Last-Modified` HTTP response header field][2] into a
[NSDate][3] object? That's no real fun, because

1. this standard formatting isn't [digested by default][4],
2. the required format string doesn't quite work as documented.

<!--more-->
  
Let's concentrate on ['Full Date' according to RFC 1123][5]:

<pre class="line-numbers"><code class="language-objc">NSString *src = @"Fri, 14 Aug 2009 14:45:31 GMT";
</code></pre>

The [NSDateFormatter][6] switched to [Unicode Format strings][7] with OSX 10.4 ([see the reference][8]), so I use

<pre class="line-numbers"><code class="language-objc">NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
df.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
</code></pre>

**Caution**: `zzz` as timezone format should give the same results, but doesn't. It says `...GMT+00:00`.

As the textual parts must be english (see [RFC 1123][5] for all the wording), I add

<pre class="line-numbers"><code class="language-objc">df.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
</code></pre>


to be independent of the user's current locale.

That's it!

**Update:**

If `[NSDateFormatter dateFromString:...]` and `[NSDateFormatter stringFromDate:...]` are threadsafe, a category-implementation could look like:

<pre class="line-numbers"><code class="language-objc">#import 

/** Category on NSDate to support rfc1123 formatted date strings.
 http://blog.mro.name/2009/08/nsdateformatter-http-header/ and
 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
 */
@interface NSDate (NSDateRFC1123)

/**
 Convert a RFC1123 'Full-Date' string
 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1)
 into NSDate.
 */
+(NSDate*)dateFromRFC1123:(NSString*)value_;

/**
 Convert NSDate into a RFC1123 'Full-Date' string
 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1).
 */
-(NSString*)rfc1123String;

@end

@implementation NSDate (NSDateRFC1123)

+(NSDate*)dateFromRFC1123:(NSString*)value_
{
    if(value_ == nil)
        return nil;
    static NSDateFormatter *rfc1123 = nil;
    if(rfc1123 == nil)
    {
        rfc1123 = [[NSDateFormatter alloc] init];
        rfc1123.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
        rfc1123.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
        rfc1123.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss z";
    }
    NSDate *ret = [rfc1123 dateFromString:value_];
    if(ret != nil)
        return ret;

    static NSDateFormatter *rfc850 = nil;
    if(rfc850 == nil)
    {
        rfc850 = [[NSDateFormatter alloc] init];
        rfc850.locale = rfc1123.locale;
        rfc850.timeZone = rfc1123.timeZone;
        rfc850.dateFormat = @"EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z";
    }
    ret = [rfc850 dateFromString:value_];
    if(ret != nil)
        return ret;

    static NSDateFormatter *asctime = nil;
    if(asctime == nil)
    {
        asctime = [[NSDateFormatter alloc] init];
        asctime.locale = rfc1123.locale;
        asctime.timeZone = rfc1123.timeZone;
        asctime.dateFormat = @"EEE MMM d HH':'mm':'ss yyyy";
    }
    return [asctime dateFromString:value_];
}

-(NSString*)rfc1123String
{
    static NSDateFormatter *df = nil;
    if(df == nil)
    {
        df = [[NSDateFormatter alloc] init];
        df.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
        df.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
        df.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
    }
    return [df stringFromDate:self];
}

@end

//////////////////////////////////////////////////////////
// A testcase to try things out.
//////////////////////////////////////////////////////////

#import 

/**
 http://blog.mro.name/2009/08/nsdateformatter-http-header/ and
 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
 */
@interface NSDateFormatterTC : SenTestCase
{

}

@end

@implementation NSDateFormatterTC

-(void)testAsctime
{
    STAssertEqualObjects(@"Sun, 06 Nov 1994 08:49:37 GMT",
        [[NSDate dateFromRFC1123:@"Sun Nov  6 08:49:37 1994"] rfc1123String], @"fail");
    STAssertEqualObjects(@"Wed, 16 Nov 1994 08:49:37 GMT",
        [[NSDate dateFromRFC1123:@"Wed Nov 16 08:49:37 1994"] rfc1123String], @"fail");
}

-(void)testRFC850
{
    STAssertEqualObjects(@"Sun, 06 Nov 1994 08:49:37 GMT",
        [[NSDate dateFromRFC1123:@"Sunday, 06-Nov-94 08:49:37 GMT"] rfc1123String], @"fail");
}

-(void)testRFC1123
{
    NSString *s = @"Fri, 14 Aug 2009 14:45:31 GMT";
    STAssertEqualObjects(s, [[NSDate dateFromRFC1123:s] rfc1123String], @"fail");

    s = @"Sun, 06 Nov 1994 08:49:37 GMT";
    STAssertEqualObjects(s, [[NSDate dateFromRFC1123:s] rfc1123String], @"fail");

    // a strange asymmetry:
    s = @"Sat, 01 Jan 0001 00:00:00 GMT";
    STAssertEqualObjects(@"Sat, 01 Jan 0001 01:27:48 GMT",
        [[NSDate dateFromRFC1123:s] rfc1123String], @"fail");
}

-(void)testRawRFC1123
{
    NSDateFormatter *rfc1123 = [[NSDateFormatter alloc] init];
    rfc1123.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
    rfc1123.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
    rfc1123.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";

    NSString *s = @"Fri, 14 Aug 2009 14:45:31 GMT";
    STAssertEqualObjects(s, [rfc1123 stringFromDate:[rfc1123 dateFromString:s]], @"fail");

    s = @"Sun, 06 Nov 1994 08:49:37 GMT";
    STAssertEqualObjects(s, [rfc1123 stringFromDate:[rfc1123 dateFromString:s]], @"fail");

    s = @"Sat, 01 Jan 0001 08:00:00 GMT";
    STAssertEqualObjects(s, [rfc1123 stringFromDate:[rfc1123 dateFromString:s]], @"fail");

    [rfc1123 release];
}
@end
</code></pre>

 [1]: http://en.wikipedia.org/wiki/Public_Domain "public domain"
 [2]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.29
 [3]: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html
 [4]: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html#//apple_ref/doc/uid/20000188-dateWithString_
 [5]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
 [6]: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/Reference/Reference.html#//apple_ref/doc/uid/20000447-SW32
 [7]: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
 [8]: http://developer.apple.com/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW5
